//
//  ViewController.m
//  RayTracer
//
//  Created by 俞云康 on 4/2/16.
//  Copyright © 2016 yushroom. All rights reserved.
//

#import "ViewController.h"
#import "MyView.h"
#include "Renderer.hpp"
#include "Timer.hpp"
#include "Math.hpp"
#include "Ray.hpp"
#include "Material.hpp"
#include "Shape.hpp"
#include "Scene.hpp"
#include "Renderer.hpp"
#include "Camera.hpp"
#include "Integrator.hpp"
#include "Log.hpp"
#include "Params.hpp"
#include "FileManager.hpp"


inline unsigned int toUInt(const Color& c) {
    unsigned int r = (unsigned int)(powf(saturate(c.r), 1/2.2f) * 255);
    unsigned int g = (unsigned int)(powf(saturate(c.g), 1/2.2f) * 255);
    unsigned int b = (unsigned int)(powf(saturate(c.b), 1/2.2f) * 255);
    //return (r << 16) | (g << 8) | (b);
    return (b << 16) | (g << 8) | (r);
    //return (b << 24) | (g << 16) | (r << 8) | 0xFF;
}

@implementation ViewController {
    MyView* mView;
    CGPoint mOrigin;
    bool selectPiexel;
    int w;
    int h;
    //unsigned int mPreviousPixels[640*480*2*2];
    vector<unsigned int> mPreviousPixels;
    int mWidth;
    int mHeight;
    
    shared_ptr<SurfaceIntegrator> mSurfaceIntegrator;
    Canvas mCanvas;
    Camera mCamera;
    shared_ptr<Scene> mScene;
    Renderer mRenderer;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    mView = (MyView*)self.view;
    
    selectPiexel = false;
    w = 0;
    h = 0;
    
    log_system_init();
}

//- (instancetype)init {
//    self = [super init];
//    return self;
//}

- (void)setRepresentedObject:(id)representedObject {
    [super setRepresentedObject:representedObject];

    // Update the view, if already loaded.
}

- (void)setXMLFilePath: (NSString*)path {
    //string xml_path = "/Users/yushroom/program/graphics/RayTracer/scenes/scene01/scene01.xml";
    static bool initlized = false;
    if (initlized) return;
    initlized = true;
    string xml_path = "/Users/yushroom/program/graphics/RayTracer/scenes/scene01/scene01.xml";
    RootParams root_params(xml_path);
    
    FileManager::setWorkingDir(FileManager::parentPath(xml_path));
    mSurfaceIntegrator = SurfaceIntegrator::create(root_params.child("integrator"));
    mCanvas = Canvas::create(root_params.child("canvas"));
    mCamera = Camera::create(root_params.child("camera"), mCanvas.getAspect());
    
    Params renderer_params = root_params.child("renderer");
    
    int camera_samples_sqrt = renderer_params.getInt("cameraSamples");
    int light_samples_sqrt = renderer_params.getInt("lightSamples");
    float report_interval = renderer_params.getFloat("reportInterval");
    mScene = Scene::create(root_params.child("scene"));
    mRenderer.init(camera_samples_sqrt, light_samples_sqrt, report_interval);
    
    WindowController * wc = self.view.window.windowController;
    NSLog(@"resize: %d %d", mCanvas.m_width, mCanvas.m_height);
    mWidth = mCanvas.m_width;
    mHeight = mCanvas.m_height;
    mPreviousPixels.resize(mWidth * mHeight * 4);
    NSSize viewFrameSize = self.view.frame.size;
    viewFrameSize.width = mWidth;
    viewFrameSize.height = mHeight;
    [self.view setFrameSize:viewFrameSize];
    [wc resize:mWidth andY:mHeight];
    //[self resize:mCanvas.width andY:mCanvas.height];
}

-(void)render {
    mRenderer.render(*mScene, mCamera, mCanvas, mSurfaceIntegrator);
    const size_t width = mCanvas.m_width;
    const size_t height = mCanvas.m_height;
    const size_t bytes = width * height * 4;
    //vector<unsigned int> p(width * 2 * height * 2);
    unsigned int* p = mPreviousPixels.data();
    for (int y = (int)height - 1; y >= 0; --y) {
        for (int x = 0; x < width; ++x) {
            unsigned int c = toUInt(mCanvas.pixels[y*width+x]);
            p[ y*2   *width*2+ x*2   ] = c;
            p[(y*2+1)*width*2+ x*2   ] = c;
            p[ y*2   *width*2+(x*2+1)] = c;
            p[(y*2+1)*width*2+(x*2+1)] = c;
        }
    }
    //MyView* view =(MyView*)self.view;
    if (mView.rep != nil) {
        unsigned int* pix = (unsigned int*)[mView.rep bitmapData];
        memcpy(pix, p, bytes * sizeof(unsigned int));
    }
    [mView setNeedsDisplay:YES];
}

- (void)renderRegion {
    if (w <= 0 || h <= 0)
        return;
    float x = float(mOrigin.x) / mWidth;
    float y = 1 - float(mOrigin.y) / mHeight;
    float ww = float(w) / mWidth;
    float hh = float(h) / mHeight;
    mRenderer.render_region(*mScene, mCamera, mCanvas, mSurfaceIntegrator, x, y, ww, hh);
    const size_t width = mCanvas.m_width;
    const size_t height = mCanvas.m_height;
    const size_t bytes = width * height * 4;
    //vector<unsigned int> p(width * 2 * height * 2);
    unsigned int* p = mPreviousPixels.data();
    for (int y = (int)height - 1; y >= 0; --y) {
        for (int x = 0; x < width; ++x) {
            unsigned int c = toUInt(mCanvas.pixels[y*width+x]);
            p[ y*2   *width*2+ x*2   ] = c;
            p[(y*2+1)*width*2+ x*2   ] = c;
            p[ y*2   *width*2+(x*2+1)] = c;
            p[(y*2+1)*width*2+(x*2+1)] = c;
        }
    }
    //MyView* view =(MyView*)self.view;
    if (mView.rep != nil) {
        unsigned int* pix = (unsigned int*)[mView.rep bitmapData];
        memcpy(pix, p, bytes * sizeof(unsigned int));
    }
    [mView setNeedsDisplay:YES];
}

- (void)renderPixel {
    selectPiexel = true;
}

- (void)clear {
    unsigned int* pix = (unsigned int*)[mView.rep bitmapData];
    const int bytes = mWidth * mHeight * 4 * 4; // 4 pixel and 4 bytes per pixel
    memset(pix, 0, bytes);
    memset(mPreviousPixels.data(), 0, bytes);
    mCanvas.clear();
    [mView setNeedsDisplay:YES];
}


#pragma mark MouseControll

//- (void)keyDown:(NSEvent *)theEvent {
//    //NSLog(@"key down");
//    NSString *key = [theEvent charactersIgnoringModifiers];
//    if ([key isEqualToString:@" "]) {
//        //setRotateMode();
//    }
//    else if ([key isEqualToString:@"f"]) {
//        //setFillMode();
//    }
//    else if ([key isEqualToString:@"n"]) {
//        //setNoraml();
//    } else if ([key isEqualToString:@"r"]) {
//        NSLog(@"redraw");
//        [mView setNeedsDisplay:YES];
//    }
//    [super keyDown:theEvent];
//}

- (void)mouseDown:(NSEvent *)theEvent {
    [super mouseDown:theEvent];
    NSPoint pos = [theEvent locationInWindow];
    [mView setNeedsDisplay:YES];
    mOrigin = pos;
    if (selectPiexel) {
        int x = round2Int(pos.x);
        int y = round2Int(pos.y);
        mRenderer.render_pixel(*mScene, mCamera, mCanvas, mSurfaceIntegrator, x, y);
        selectPiexel = false;
    }
}

- (void)mouseDragged:(NSEvent *)theEvent {
    [super mouseDragged:theEvent];
    unsigned int* pix = (unsigned int*)[mView.rep bitmapData];
    const int bytes = mWidth * mHeight * 4 * 4;
    memcpy(pix, mPreviousPixels.data(), bytes);
    CGPoint pos = [theEvent locationInWindow];
    int dx = (pos.x > mOrigin.x) ? 1 : -1;
    int dy = (pos.y > mOrigin.y) ? 1 : -1;
    int ox = round2Int(mOrigin.x);
    int oy = mHeight - round2Int(mOrigin.y);
    int x = round2Int(pos.x);
    int y = mHeight - round2Int(pos.y);
    if (x < 0) x = 0;
    if (x >= mWidth) x = mWidth-1;
    if (y < 0) y = 0;
    if (y >= mHeight) y = mHeight-1;
    w = abs(x - ox)+1;
    h = abs(y - oy)+1;
    for (int ix = 0; ix < w; ++ix) {
        pix[oy*2*mWidth*2 + (ox+dx*ix)*2] = 0xFF;
        pix[ y*2*mWidth*2 + (ox+dx*ix)*2] = 0xFF;
    }
    for (int iy = 0; iy < h; ++iy) {
        pix[(oy-dy*iy)*2*mWidth*2 + ox*2] = 0xFF;
        pix[ (y+dy*iy)*2*mWidth*2 + x*2] = 0xFF;
    }
    [mView setNeedsDisplay:YES];
}

//- (void)mouseUp:(NSEvent *)theEvent {
//    [mView setNeedsDisplay:YES];
//}

@end
